1   /*
2    * Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  package com.sun.media.sound;
26  
27  import java.io.IOException;
28  import java.util.Arrays;
29  
30  import javax.sound.midi.MidiChannel;
31  import javax.sound.midi.VoiceStatus;
32  
33  /**
34   * Abstract resampler class.
35   *
36   * @author Karl Helgason
37   */
38  public abstract class SoftAbstractResampler implements SoftResampler {
39  
40      private class ModelAbstractResamplerStream implements SoftResamplerStreamer {
41  
42          AudioFloatInputStream stream;
43          boolean stream_eof = false;
44          int loopmode;
45          boolean loopdirection = true; // true = forward
46          float loopstart;
47          float looplen;
48          float target_pitch;
49          float[] current_pitch = new float[1];
50          boolean started;
51          boolean eof;
52          int sector_pos = 0;
53          int sector_size = 400;
54          int sector_loopstart = -1;
55          boolean markset = false;
56          int marklimit = 0;
57          int streampos = 0;
58          int nrofchannels = 2;
59          boolean noteOff_flag = false;
60          float[][] ibuffer;
61          boolean ibuffer_order = true;
62          float[] sbuffer;
63          int pad;
64          int pad2;
65          float[] ix = new float[1];
66          int[] ox = new int[1];
67          float samplerateconv = 1;
68          float pitchcorrection = 0;
69  
70          public ModelAbstractResamplerStream() {
71              pad = getPadding();
72              pad2 = getPadding() * 2;
73              ibuffer = new float[2][sector_size + pad2];
74              ibuffer_order = true;
75          }
76  
77          public void noteOn(MidiChannel channel, VoiceStatus voice,
78                  int noteNumber, int velocity) {
79          }
80  
81          public void noteOff(int velocity) {
82              noteOff_flag = true;
83          }
84  
85          public void open(ModelWavetable osc, float outputsamplerate)
86                  throws IOException {
87  
88              eof = false;
89              nrofchannels = osc.getChannels();
90              if (ibuffer.length < nrofchannels) {
91                  ibuffer = new float[nrofchannels][sector_size + pad2];
92              }
93  
94              stream = osc.openStream();
95              streampos = 0;
96              stream_eof = false;
97              pitchcorrection = osc.getPitchcorrection();
98              samplerateconv
99                      = stream.getFormat().getSampleRate() / outputsamplerate;
100             looplen = osc.getLoopLength();
101             loopstart = osc.getLoopStart();
102             sector_loopstart = (int) (loopstart / sector_size);
103             sector_loopstart = sector_loopstart - 1;
104 
105             sector_pos = 0;
106 
107             if (sector_loopstart < 0)
108                 sector_loopstart = 0;
109             started = false;
110             loopmode = osc.getLoopType();
111 
112             if (loopmode != 0) {
113                 markset = false;
114                 marklimit = nrofchannels * (int) (looplen + pad2 + 1);
115             } else
116                 markset = true;
117             // loopmode = 0;
118 
119             target_pitch = samplerateconv;
120             current_pitch[0] = samplerateconv;
121 
122             ibuffer_order = true;
123             loopdirection = true;
124             noteOff_flag = false;
125 
126             for (int i = 0; i < nrofchannels; i++)
127                 Arrays.fill(ibuffer[i], sector_size, sector_size + pad2, 0);
128             ix[0] = pad;
129             eof = false;
130 
131             ix[0] = sector_size + pad;
132             sector_pos = -1;
133             streampos = -sector_size;
134 
135             nextBuffer();
136         }
137 
138         public void setPitch(float pitch) {
139             /*
140             this.pitch = (float) Math.pow(2f,
141             (pitchcorrection + pitch) / 1200.0f)
142              * samplerateconv;
143              */
144             this.target_pitch = (float)Math.exp(
145                     (pitchcorrection + pitch) * (Math.log(2.0) / 1200.0))
146                 * samplerateconv;
147 
148             if (!started)
149                 current_pitch[0] = this.target_pitch;
150         }
151 
152         public void nextBuffer() throws IOException {
153             if (ix[0] < pad) {
154                 if (markset) {
155                     // reset to target sector
156                     stream.reset();
157                     ix[0] += streampos - (sector_loopstart * sector_size);
158                     sector_pos = sector_loopstart;
159                     streampos = sector_pos * sector_size;
160 
161                     // and go one sector backward
162                     ix[0] += sector_size;
163                     sector_pos -= 1;
164                     streampos -= sector_size;
165                     stream_eof = false;
166                 }
167             }
168 
169             if (ix[0] >= sector_size + pad) {
170                 if (stream_eof) {
171                     eof = true;
172                     return;
173                 }
174             }
175 
176             if (ix[0] >= sector_size * 4 + pad) {
177                 int skips = (int)((ix[0] - sector_size * 4 + pad) / sector_size);
178                 ix[0] -= sector_size * skips;
179                 sector_pos += skips;
180                 streampos += sector_size * skips;
181                 stream.skip(sector_size * skips);
182             }
183 
184             while (ix[0] >= sector_size + pad) {
185                 if (!markset) {
186                     if (sector_pos + 1 == sector_loopstart) {
187                         stream.mark(marklimit);
188                         markset = true;
189                     }
190                 }
191                 ix[0] -= sector_size;
192                 sector_pos++;
193                 streampos += sector_size;
194 
195                 for (int c = 0; c < nrofchannels; c++) {
196                     float[] cbuffer = ibuffer[c];
197                     for (int i = 0; i < pad2; i++)
198                         cbuffer[i] = cbuffer[i + sector_size];
199                 }
200 
201                 int ret;
202                 if (nrofchannels == 1)
203                     ret = stream.read(ibuffer[0], pad2, sector_size);
204                 else {
205                     int slen = sector_size * nrofchannels;
206                     if (sbuffer == null || sbuffer.length < slen)
207                         sbuffer = new float[slen];
208                     int sret = stream.read(sbuffer, 0, slen);
209                     if (sret == -1)
210                         ret = -1;
211                     else {
212                         ret = sret / nrofchannels;
213                         for (int i = 0; i < nrofchannels; i++) {
214                             float[] buff = ibuffer[i];
215                             int ix = i;
216                             int ix_step = nrofchannels;
217                             int ox = pad2;
218                             for (int j = 0; j < ret; j++, ix += ix_step, ox++)
219                                 buff[ox] = sbuffer[ix];
220                         }
221                     }
222 
223                 }
224 
225                 if (ret == -1) {
226                     ret = 0;
227                     stream_eof = true;
228                     for (int i = 0; i < nrofchannels; i++)
229                         Arrays.fill(ibuffer[i], pad2, pad2 + sector_size, 0f);
230                     return;
231                 }
232                 if (ret != sector_size) {
233                     for (int i = 0; i < nrofchannels; i++)
234                         Arrays.fill(ibuffer[i], pad2 + ret, pad2 + sector_size, 0f);
235                 }
236 
237                 ibuffer_order = true;
238 
239             }
240 
241         }
242 
243         public void reverseBuffers() {
244             ibuffer_order = !ibuffer_order;
245             for (int c = 0; c < nrofchannels; c++) {
246                 float[] cbuff = ibuffer[c];
247                 int len = cbuff.length - 1;
248                 int len2 = cbuff.length / 2;
249                 for (int i = 0; i < len2; i++) {
250                     float x = cbuff[i];
251                     cbuff[i] = cbuff[len - i];
252                     cbuff[len - i] = x;
253                 }
254             }
255         }
256 
257         public int read(float[][] buffer, int offset, int len)
258                 throws IOException {
259 
260             if (eof)
261                 return -1;
262 
263             if (noteOff_flag)
264                 if ((loopmode & 2) != 0)
265                     if (loopdirection)
266                         loopmode = 0;
267 
268 
269             float pitchstep = (target_pitch - current_pitch[0]) / len;
270             float[] current_pitch = this.current_pitch;
271             started = true;
272 
273             int[] ox = this.ox;
274             ox[0] = offset;
275             int ox_end = len + offset;
276 
277             float ixend = sector_size + pad;
278             if (!loopdirection)
279                 ixend = pad;
280             while (ox[0] != ox_end) {
281                 nextBuffer();
282                 if (!loopdirection) {
283                     // If we are in backward playing part of pingpong
284                     // or reverse loop
285 
286                     if (streampos < (loopstart + pad)) {
287                         ixend = loopstart - streampos + pad2;
288                         if (ix[0] <= ixend) {
289                             if ((loopmode & 4) != 0) {
290                                 // Ping pong loop, change loopdirection
291                                 loopdirection = true;
292                                 ixend = sector_size + pad;
293                                 continue;
294                             }
295 
296                             ix[0] += looplen;
297                             ixend = pad;
298                             continue;
299                         }
300                     }
301 
302                     if (ibuffer_order != loopdirection)
303                         reverseBuffers();
304 
305                     ix[0] = (sector_size + pad2) - ix[0];
306                     ixend = (sector_size + pad2) - ixend;
307                     ixend++;
308 
309                     float bak_ix = ix[0];
310                     int bak_ox = ox[0];
311                     float bak_pitch = current_pitch[0];
312                     for (int i = 0; i < nrofchannels; i++) {
313                         if (buffer[i] != null) {
314                             ix[0] = bak_ix;
315                             ox[0] = bak_ox;
316                             current_pitch[0] = bak_pitch;
317                             interpolate(ibuffer[i], ix, ixend, current_pitch,
318                                     pitchstep, buffer[i], ox, ox_end);
319                         }
320                     }
321 
322                     ix[0] = (sector_size + pad2) - ix[0];
323                     ixend--;
324                     ixend = (sector_size + pad2) - ixend;
325 
326                     if (eof) {
327                         current_pitch[0] = this.target_pitch;
328                         return ox[0] - offset;
329                     }
330 
331                     continue;
332                 }
333                 if (loopmode != 0) {
334                     if (streampos + sector_size > (looplen + loopstart + pad)) {
335                         ixend = loopstart + looplen - streampos + pad2;
336                         if (ix[0] >= ixend) {
337                             if ((loopmode & 4) != 0 || (loopmode & 8) != 0) {
338                                 // Ping pong or revese loop, change loopdirection
339                                 loopdirection = false;
340                                 ixend = pad;
341                                 continue;
342                             }
343                             ixend = sector_size + pad;
344                             ix[0] -= looplen;
345                             continue;
346                         }
347                     }
348                 }
349 
350                 if (ibuffer_order != loopdirection)
351                     reverseBuffers();
352 
353                 float bak_ix = ix[0];
354                 int bak_ox = ox[0];
355                 float bak_pitch = current_pitch[0];
356                 for (int i = 0; i < nrofchannels; i++) {
357                     if (buffer[i] != null) {
358                         ix[0] = bak_ix;
359                         ox[0] = bak_ox;
360                         current_pitch[0] = bak_pitch;
361                         interpolate(ibuffer[i], ix, ixend, current_pitch,
362                                 pitchstep, buffer[i], ox, ox_end);
363                     }
364                 }
365 
366                 if (eof) {
367                     current_pitch[0] = this.target_pitch;
368                     return ox[0] - offset;
369                 }
370             }
371 
372             current_pitch[0] = this.target_pitch;
373             return len;
374         }
375 
376         public void close() throws IOException {
377             stream.close();
378         }
379     }
380 
381     public abstract int getPadding();
382 
383     public abstract void interpolate(float[] in, float[] in_offset,
384             float in_end, float[] pitch, float pitchstep, float[] out,
385             int[] out_offset, int out_end);
386 
387     public SoftResamplerStreamer openStreamer() {
388         return new ModelAbstractResamplerStream();
389     }
390 }